

#include "autoconfig.h"

#if defined(CONFIG_NET_ETH)
#include "eth.h"
#include "qe_arch.h"
#include "qe_def.h"



struct eth_device *eth_current;
static xTaskHandle txtsk;
static xTaskHandle rxtsk;
static QueueHandle_t rx_queue = NULL;
static QueueHandle_t tx_wake = NULL;
static QueueHandle_t tx_queue = NULL;

struct eth_pkg_msg {
    qe_u32 addr;
    qe_u32 size;
};


struct eth_device *eth_get_dev(void)
{
	return eth_current;
}

qe_ret eth_queue_xmit(void *pkt, int len)
{
    if (!eth_current)
        return qe_err_notsupport;
    struct eth_pkg_msg msg = {
        .addr = (qe_u32)pkt,
        .size = len
    };
    xQueueSend(tx_queue, &msg, pdMS_TO_TICKS(10));
    return qe_ok;
}

qe_ret eth_send(void *pkt, int len)
{
    if (!eth_current)
        return qe_err_notsupport;
    return eth_current->start_xmit(eth_current, pkt, len);
}

qe_ret eth_write_hwaddr(struct eth_device *dev, qe_u8 hwaddr[6])
{
    if (dev->set_macaddr) {
        qe_memcpy(dev->enetaddr, hwaddr, 6);
        return dev->set_macaddr(dev);
    }

    return qe_err_notexist;
}

qe_ret eth_register(struct eth_device *eth, const char *name, qe_u32 flag, void *data)
{
    struct qe_device *dev;

    if (!eth_current)
        eth_current = eth;

    dev = &(eth->parent);

    dev->type = QE_DevClass_NetIf;
    dev->rx_indicate = QE_NULL;
    dev->tx_complete = QE_NULL;
    dev->priv = eth;

    return qe_dev_register(dev, name, flag);
}

qe_ret eth_wakeup_queue(void)
{
    BaseType_t woken = pdFALSE;
    char waek;
    xQueueSendFromISR(tx_wake, &waek, &woken);
    //portYIELD_FROM_ISR(woken);
    return qe_ok;
}

void eth_receive(void *pkt, int len)
{
    BaseType_t woken = pdFALSE;
    struct eth_pkg_msg msg = {(qe_u32)pkt, len};
    xQueueSendFromISR(rx_queue, &msg, &woken);
    //portYIELD_FROM_ISR(woken);
}

static void eth_rx_work(void *ref)
{
    struct eth_pkg_msg msg;

    rx_queue = xQueueCreate(8, sizeof(struct eth_pkg_msg));
    net_debug("eth rx work ready");

    while (1) {
        if (pdPASS == xQueueReceive(rx_queue, &msg, portMAX_DELAY)) {
            net_receive((qe_u8 *)msg.addr, msg.size);
        }
    }
}

static void eth_tx_work(void *ref)
{
    qe_ret ret;
    char waek;
    struct eth_pkg_msg msg;

    tx_wake = xQueueCreate(16, sizeof(char));
    tx_queue = xQueueCreate(16, sizeof(struct eth_pkg_msg));
    net_debug("eth tx work ready");

    while (1) {

        if (pdPASS == xQueuePeek(tx_queue, &msg, portMAX_DELAY)) {
			ret = eth_current->start_xmit(eth_current, (void *)msg.addr, msg.size);
			if (ret != qe_ok) {
				eth_current->stats.tx_errors++;
			} else {
				xQueueReceive(tx_queue, &msg, 0);
			}
        }

        xQueueReceive(tx_wake, &waek, pdMS_TO_TICKS(1000));
    }
}

void eth_initialize(void)
{
    net_debug("eth init");

    xTaskCreate(eth_tx_work, "tx", 200, QE_NULL, CONFIG_NET_TX_TASK_PRIOR, &txtsk);
    xTaskCreate(eth_rx_work, "rx", 400, QE_NULL, CONFIG_NET_RX_TASK_PRIOR, &rxtsk);

    if (!eth_current) {
        net_notice("no ethernet found");
    } else {
        eth_current->init(eth_current);
    }
}
#endif
